Abraxus's Blog

Angstrom Infinity Gauntlet Write Up

Details:

Points: 75

Jeopardy style CTF

Category: Reverse Engineering

Comments:

All clam needs to do is snap and finite will turn into infinite...

Find it on the shell server at /problems/2021/infinity_gauntlet or over netcat at nc shell.actf.co 21700

Write up:

Reversing the main function we get:

  v32 = __readfsqword(0x28u);
  setvbuf(_bss_start, 0LL, 2, 0LL);
  v3 = fopen("flag.txt", "r");
  if ( v3 )
  {
    v4 = v3;
    fgets(s, 256, v3);
    fclose(v4);
    v5 = strcspn(s, "\n");
    v6 = v5;
    s[v5] = 0;
    if ( v5 )
    {
      v7 = s;
      v8 = 17 * v5;
      v9 = 0;
      do
      {
        *v7 ^= v9;
        v9 += 17;
        ++v7;
      }
      while ( (_BYTE)v9 != v8 );
    }
    v10 = 1;
    v11 = time(0LL);
    srand(v11);
    puts("Welcome to the infinity gauntlet!");
    puts("If you complete the gauntlet, you'll get the flag!");
    while ( 1 )
    {
      printf("=== ROUND %d ===\n", (unsigned int)v10);
      v14 = rand();
      v15 = v10 > 49 ? (unsigned __int8)s[v14 % v6] | ((unsigned __int8)(v14 % v6 + v10) << 8) : rand() % 0x10000;
      if ( (rand() & 1) != 0 )
      {
        v12 = rand() % 3;
        if ( v12 )
        {
          if ( v12 == 1 )
          {
            v22 = rand();
            printf("foo(%u, ?) = %u\n", (unsigned int)(v22 % 1337), (v22 % 1337) ^ (v15 + 1) ^ 0x539);
          }
          else
          {
            v13 = rand();
            printf("foo(%u, %u) = ?\n", v15 ^ (v13 % 1337 + 1) ^ 0x539, (unsigned int)(v13 % 1337));
          }
        }
        else
        {
          v19 = rand();
          printf("foo(?, %u) = %u\n", (unsigned int)(v19 % 1337), v15 ^ (v19 % 1337 + 1) ^ 0x539);
        }
      }
      else
      {
        v16 = rand();
        if ( (v16 & 3) != 0 )
        {
          v17 = v16 % 4;
          if ( v17 == 1 )
          {
            v24 = rand() % 1337;
            v25 = rand();
            v26 = (unsigned int)(v25 >> 31);
            LODWORD(v26) = v25 % 1337;
            printf("bar(%u, ?, %u) = %u\n", v24, v26, v24 + v15 * (v25 % 1337 + 1));
          }
          else if ( v17 == 2 )
          {
            v27 = rand() % 1337;
            v28 = rand();
            v29 = (unsigned int)(v28 >> 31);
            LODWORD(v29) = v28 % 1337;
            printf("bar(%u, %u, ?) = %u\n", v27, v29, v27 + v28 % 1337 * (v15 + 1));
          }
          else
          {
            v18 = v15 <= 0x539 ? rand() % v15 : rand() % 1337;
            printf("bar(%u, %u, %u) = ?\n", v15 % v18, v18, v15 / v18 - 1);
          }
        }
        else
        {
          v20 = rand() % 1337;
          v21 = rand();
          printf("bar(?, %u, %u) = %u\n", v20, (unsigned int)(v21 % 1337), v15 + v20 * (v21 % 1337 + 1));
        }
      }
      __isoc99_scanf("%u", &v30);
      if ( v30 != v15 )
        break;
      printf("Correct! Maybe round %d will get you the flag ;)\n", (unsigned int)++v10);
    }
    puts("Wrong!");
    result = 0;
  }

The first thing I did was reverse the various foo and bar functions to figure out what value I needed to type:

def foo1(a, c):
	return (a^c^0x539)-1

def foo2(a, b):
	return a^(b+1)^0x539

def foo3(b, c):
	return (b+1)^c^0x539

def bar1(a, c, d):
	return (d-a)/((c&0xFFF)+1)

def bar2(a, b, d):
	if b == 0:
		return -1
	return ((d-a)/((b&0xFFF)))-1


def bar3(a, b, c):
	return ((c+1)*b)+a

def bar4(b, c, d):
	return d-(b*(c+1))

v15 was the value that we got each time, this value was set on this line:

v15 = v10 > 49 ? (unsigned __int8)s[v14 % v6] | ((unsigned __int8)(v14 % v6 + v10) << 8) : rand() % 0x10000;

Using this we can get the original value from s as well as the position value. So if we do a bunch of iterations of the problems we can theoretically solve for all the values.

s is the encrypted flag that gets set here:

    v5 = strcspn(s, "\n");
    v6 = v5;
    s[v5] = 0;
    if ( v5 )
    {
      v7 = s;
      v8 = 17 * v5;
      v9 = 0;
      do
      {
        *v7 ^= v9;
        v9 += 17;
        ++v7;
      }
      while ( (_BYTE)v9 != v8 );
    }

Which can be reversed fairly simply by using the position value plus 1 times 17 xor'ed with the value we received. I then made the following script:

# pwntools import
from pwn import *

# reversed functions
def foo1(a, c):
	return (a^c^0x539)-1

def foo2(a, b):
	return a^(b+1)^0x539

def foo3(b, c):
	return (b+1)^c^0x539

def bar1(a, c, d):
	return (d-a)/((c&0xFFF)+1)

def bar2(a, b, d):
	if b == 0:
		return -1
	return ((d-a)/((b&0xFFF)))-1

def bar3(a, b, c):
	return ((c+1)*b)+a

def bar4(b, c, d):
	return d-(b*(c+1))

# open remote shell
r = remote('shell.actf.co', 21700)

print(r.recvline())
print(r.recvline())

i = 1

solved = []

clen = 50

new4 = [0 for i in range(0, clen+1)]

while True:
	if i > 30000:
		break


	r.recvline()
	x = r.recvline()
	t = x.decode().split('(')[1].split(')')[0]
	t=t.replace(' ','').split(',')
	t.append(x[x.decode().index('= ')+2:x.decode().index('\n')].decode())
	ind = t.index('?')
	ans = 0

    # answer the question
	if 'foo' in x.decode():
		if ind == 0:
			ans = foo3(int(t[1]),int(t[2]))
		elif ind == 1:
			ans = foo1(int(t[0]),int(t[2]))
		else:
			ans = foo2(int(t[0]),int(t[1]))
	else:
		if ind == 0:
			ans = bar4(int(t[1]), int(t[2]), int(t[3]))
		elif ind == 1:
			ans = bar1(int(t[0]), int(t[2]), int(t[3]))
		elif ind == 2:
			ans = bar2(int(t[0]), int(t[1]), int(t[3]))
		else:
			ans = bar3(int(t[0]), int(t[1]), int(t[2]))
	r.send(str(int(math.ceil(ans)+2**32)) + '\n')
	r.recvline()
	i += 1

    # if i greater than 50, this is a check on their side
	if i > 50:
		ans = int(ans)

        # get position
		a = ((((((ans & 0xFF00) >> 8)))&0xFF)-i)

        # if not already in the solved
		if a > 0 and a < clen and a not in solved:
			solved.append(a)

            # get encrypted value
			b = ((ans | ((a)+i) << 8)&0x00FF)

            # decrypt and print
			solved.append(chr(0xFF&(((a+1)*17)^b)))
			new4[a] = chr(0xFF&(((a+1)*17)^b))
			print(new4)

After letting it run for a few seconds we get:

[+] Opening connection to shell.actf.co on port 21700: Done
b'Welcome to the infinity gauntlet!\n'
b"If you complete the gauntlet, you'll get the flag!\n"
[0, 0, 0, '{', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, '{', 0, 0, 0, 0, 'p', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, '{', 0, 0, 0, 0, 'p', 0, 0, 0, 'a', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, '{', 0, 0, 0, 0, 'p', 0, 0, 0, 'a', 0, 'a', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 0, '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 0, 0, 0, 0, 0, 0, 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 'y', 0, 0, 0, 0, 0, 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 0, 'a', 'y', '_', 0, 0, 0, 0, 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 'w', 'a', 'y', '_', 0, 0, 0, 0, 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 'w', 'a', 'y', '_', 0, 0, 0, '_', 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 0, 'p', 'e', 0, 0, 'a', 'w', 'a', 'y', '_', 't', 0, 0, '_', 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 'p', 'p', 'e', 0, 0, 'a', 'w', 'a', 'y', '_', 't', 0, 0, '_', 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 'p', 'p', 'e', 0, '_', 'a', 'w', 'a', 'y', '_', 't', 0, 0, '_', 0, 'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 'p', 'p', 'e', 0, '_', 'a', 'w', 'a', 'y', '_', 't', 0, 0, '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 0, 0, 0, 'p', 'p', 'e', 0, '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 0, '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 0, 0, 'p', 'p', 'e', 0, '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 0, '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 0, 0, 'p', 'p', 'e', 0, '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 0, 0, 'p', 'p', 'e', 'd', '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 'n', 0, 'p', 'p', 'e', 'd', '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 'n', 'a', 'p', 'p', 'e', 'd', '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 0, 'n', 0, '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 'n', 'a', 'p', 'p', 'e', 'd', '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 0, 'n', 'd', '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 't', 'f', '{', 's', 'n', 'a', 'p', 'p', 'e', 'd', '_', 'a', 'w', 'a', 'y', '_', 't', 'h', 'e', '_', 'e', 'n', 'd', '}', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

The flag ended up being:

actf{snapped_away_the_end}